﻿using System;
using System.Data;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net;
using System.IO;
using System.Collections.Specialized;

/// <summary>
/// Summary description for ClavidOAuth
///
/// $Id: ClavidOAuth.cs 13 2011-10-09 15:50:26Z telbie $
/// </summary>
public class ClavidOAuth : OAuthBase
{
    public enum Method { GET, POST, DELETE };
    public const string REQUEST_TOKEN = "https://www.clavid.com/provider/oauth/request_token";
    public const string AUTHORIZE = "https://www.clavid.com/provider/oauth/authorize";
    public const string ACCESS_TOKEN = "https://www.clavid.com/provider/oauth/access_token";

    private string _consumerKey = "";
    private string _consumerSecret = "";
    private string _token = "";
    private string _tokenSecret = "";
    private string _callBackUrl = "oob";
    private string _oauthVerifier = "";

    #region Properties
    public string ConsumerKey
    {
        get
        {
            if (_consumerKey.Length == 0)
            {
                _consumerKey = ConfigurationManager.AppSettings["consumerKey"];
            }
            return _consumerKey;
        }
        set { _consumerKey = value; }
    }

    public string ConsumerSecret
    {
        get
        {
            if (_consumerSecret.Length == 0)
            {
                _consumerSecret = ConfigurationManager.AppSettings["consumerSecret"];
            }
            return _consumerSecret;
        }
        set { _consumerSecret = value; }
    }

    public string Token { get { return _token; } set { _token = value; } }
    public string TokenSecret { get { return _tokenSecret; } set { _tokenSecret = value; } }
    public string CallBackUrl { get { return _callBackUrl; } set { _callBackUrl = value; } }
    public string OAuthVerifier { get { return _oauthVerifier; } set { _oauthVerifier = value; } }

    #endregion

    /// <summary>
    /// Get the link to Clavid's authorization page for this application.
    /// </summary>
    /// <returns>The url with a valid request token, or a null string.</returns>
    public string AuthorizationLinkGet(out string tokenSecret)
    {
        string ret = null;

        string url = REQUEST_TOKEN;
        Dictionary<String, String> parameters = getRequestTokenParameters();
        string par = "";
        foreach (KeyValuePair<String, String> pair in parameters)
        {
            if (!String.IsNullOrEmpty(par))
            {
                par = par + "&";
            }
            par = par + pair.Key;
            par = par + "=";
            par = par + pair.Value;
        }
        if (url.IndexOf("?") > 0)
        {
            url += "&";
        }
        else
        {
            url += "?";
        }
        url += par;
        tokenSecret = null;
        string response = oAuthWebRequest(Method.GET, url, this.CallBackUrl, String.Empty);
        if (response.Length > 0)
        {
            //response contains token and token secret.  We only need the token.
            NameValueCollection qs = HttpUtility.ParseQueryString(response);

            if (qs["oauth_callback_confirmed"] != null)
            {
                if (qs["oauth_callback_confirmed"] != "true")
                {
                    throw new Exception("OAuth callback not confirmed.");
                }
            }

            if (qs["oauth_token"] != null)
            {
                ret = AUTHORIZE + "?oauth_token=" + qs["oauth_token"];
            }
            if (qs["oauth_token_secret"] != null)
            {
                this.TokenSecret = qs["oauth_token_secret"];
                tokenSecret = this.TokenSecret;
            }
        }
        return ret;
    }

    /// <summary>
    /// Exchange the request token for an access token.
    /// </summary>
    /// <param name="authToken">The oauth_token is supplied by Clavid's authorization page following the callback.</param>
    /// <param name="oauthVerifier">An oauth_verifier parameter is provided to the client either in the pre-configured callback URL</param>
    public void AccessTokenGet(string tokenSecret, string authToken, string oauthVerifier)
    {
        this.TokenSecret = tokenSecret;
        this.Token = authToken;
        this.OAuthVerifier = oauthVerifier;

        string url = ACCESS_TOKEN;

        string response = oAuthWebRequest(Method.GET, url, null, String.Empty);

        if (response.Length > 0)
        {
            NameValueCollection qs = HttpUtility.ParseQueryString(response);
            setResponseParameters(qs);
        }
    }

    /// <summary>
    /// Submit a web request using oAuth.
    /// </summary>
    /// <param name="method">GET or POST</param>
    /// <param name="url">The full url, including the querystring.</param>
    /// <param name="postData">Data to post (querystring format)</param>
    /// <returns>The web server response.</returns>
    public string oAuthWebRequest(Method method, string url, string callBackUrl, string postData)
    {
        string outUrl = "";
        string querystring = "";
        string ret = "";


        //Setup postData for signing.
        //Add the postData to the querystring.
        if (method == Method.POST || method == Method.DELETE)
        {
            if (postData.Length > 0)
            {
                //Decode the parameters and re-encode using the oAuth UrlEncode method.
                NameValueCollection qs = HttpUtility.ParseQueryString(postData);
                postData = "";
                foreach (string key in qs.AllKeys)
                {
                    if (postData.Length > 0)
                    {
                        postData += "&";
                    }
                    qs[key] = HttpUtility.UrlDecode(qs[key]);
                    qs[key] = this.UrlEncode(qs[key]);
                    postData += key + "=" + qs[key];

                }
                if (url.IndexOf("?") > 0)
                {
                    url += "&";
                }
                else
                {
                    url += "?";
                }
                url += postData;
            }
        }
        Uri uri = new Uri(url);

        string nonce = this.GenerateNonce();
        string timeStamp = this.GenerateTimeStamp();

        //Generate Signature
        string sig = this.GenerateSignature(uri,
            this.ConsumerKey,
            this.ConsumerSecret,
            this.Token,
            this.TokenSecret,
            callBackUrl,
            this.OAuthVerifier,
            method.ToString(),
            timeStamp,
            nonce,
            out outUrl,
            out querystring);

        querystring += "&oauth_signature=" + this.UrlEncode(sig);

        //Convert the querystring to postData
        if (method == Method.POST || method == Method.DELETE)
        {
            postData = querystring;
            querystring = "";
        }

        if (querystring.Length > 0)
        {
            outUrl += "?";
        }

        ret = WebRequest(method, outUrl + querystring, postData);

        return ret;
    }

    /// <summary>
    /// Web Request Wrapper
    /// </summary>
    /// <param name="method">Http Method</param>
    /// <param name="url">Full url to the web resource</param>
    /// <param name="postData">Data to post in querystring format</param>
    /// <returns>The web server response.</returns>
    public string WebRequest(Method method, string url, string postData)
    {
        HttpWebRequest webRequest = null;
        StreamWriter requestWriter = null;
        string responseData = "";

        webRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
        webRequest.Method = method.ToString();
        webRequest.ServicePoint.Expect100Continue = false;
        //webRequest.UserAgent  = "Identify your application please.";
        //webRequest.Timeout = 20000;

        if (method == Method.POST || method == Method.DELETE)
        {
            webRequest.ContentType = "application/x-www-form-urlencoded";

            //POST the data.
            requestWriter = new StreamWriter(webRequest.GetRequestStream());
            try
            {
                requestWriter.Write(postData);
            }
            catch
            {
                throw;
            }
            finally
            {
                requestWriter.Close();
                requestWriter = null;
            }
        }

        responseData = WebResponseGet(webRequest);

        webRequest = null;

        return responseData;

    }

    /// <summary>
    /// Process the web response.
    /// </summary>
    /// <param name="webRequest">The request object.</param>
    /// <returns>The response data.</returns>
    public string WebResponseGet(HttpWebRequest webRequest)
    {
        Stream stream = null;
        StreamReader responseReader = null;
        string responseData = "";

        try
        {
            WebResponse res = webRequest.GetResponse();
            stream = res.GetResponseStream();
            responseReader = new StreamReader(stream);
            responseData = responseReader.ReadToEnd();
        }
        catch
        {
            throw;
        }
        finally
        {
            if (stream != null) stream.Close();
            stream = null;
            if (responseReader != null) responseReader.Close();
            responseReader = null;
        }

        return responseData;
    }

    public virtual Dictionary<String,String> getRequestTokenParameters()
    {
        return new Dictionary<String,String>();
    }


    public virtual void setResponseParameters(NameValueCollection parameters)
    {
    }

}